
// only if the Inst Buffer is ready , the IFU ADDR Gen can work 

module frv_ifu_reqgen (
    input                   clk             ,
    input                   rst_n           ,
    // Program Downloader reset
    input                   pd_rst          ,
    // Branch Prediction 
    input [31:0]            bp_taddr        ,
    input                   bp_branch_taken ,
    input [3:0]             bp_bhtv         ,
    input [31:0]            bp_phtv         ,
    // Exception Interface 
    input                   exp_flush       ,               
    input [31:0]            exp_taddr       ,               
    // downstream modoule control
    input                   bru_flush       ,
    input [31:0]            bru_btb_taddr   ,           
    input                   ib_ready        ,
    //Output to ICache
    output                  imem_req        ,
    output [31:0]           imem_addr       , 
    // Output to IBUF
    output                  ifu_ib_wreq     ,
    output [31:0]           ifu_ib_pc_data  ,
    output                  ifu_ib_exp_vld  ,
    output [4:0]            ifu_ib_exp_code ,
    output [31:0]           ifu_ib_bp_taddr ,
    output                  ifu_ib_bp_taken ,
    output [3:0]            ifu_ib_bp_bhtv  , // BHT Entry Value
    output [31:0]           ifu_ib_bp_phtv   // PHT Entry Value       
);

wire [31:0]           pc_data,pc_data_nxt;

wire [31:0]           inst_addr,inst_addr_nxt;
wire                  iaddr_vld,iaddr_vld_nxt;
wire                  ifetch_en;

wire                  ib_wreq     ,ib_wreq_buf    ,ib_wreq_nxt    ;
wire [31:0]           ib_pc_data  ,ib_pc_data_buf ,ib_pc_data_nxt ;
wire                  ib_exp_vld  ,ib_exp_vld_buf ,ib_exp_vld_nxt ;
wire [4:0]            ib_exp_code ,ib_exp_code_buf,ib_exp_code_nxt;
wire [31:0]           ib_bp_taddr ,ib_bp_taddr_buf,ib_bp_taddr_nxt;
wire                  ib_bp_taken ,ib_bp_taken_buf,ib_bp_taken_nxt;
wire [3:0]            ib_bp_bhtv  ,ib_bp_bhtv_buf ,ib_bp_bhtv_nxt ;
wire [31:0]           ib_bp_phtv  ,ib_bp_phtv_buf ,ib_bp_phtv_nxt ;

wire [31:0] target_addr;

assign target_addr  = pd_rst ? 32'h8010_0000 :
                           exp_flush ? exp_taddr : 
                           bru_flush ? bru_btb_taddr  :
                           bp_branch_taken ? bp_taddr :
                           pc_data ;

assign pc_data_nxt  = target_addr + 4;

assign inst_addr_nxt = (exp_flush | bru_flush | bp_branch_taken) ? target_addr : pc_data;

assign iaddr_vld_nxt = (ib_ready && ~pd_rst) || bru_flush;

assign ifetch_en = ib_ready && ~pd_rst;

assign ib_wreq_nxt      = iaddr_vld && ~bru_flush && ib_ready; 
assign ib_pc_data_nxt   = inst_addr              ; 
assign ib_exp_vld_nxt   = exp_flush              ; 
assign ib_exp_code_nxt  = 0                      ; 
assign ib_bp_taddr_nxt  = bp_taddr               ; 
assign ib_bp_taken_nxt  = bp_branch_taken        ; 
assign ib_bp_bhtv_nxt   = bp_bhtv                ; 
assign ib_bp_phtv_nxt   = bp_phtv                ; 

// Port Connect
assign imem_req  = iaddr_vld;
assign imem_addr = inst_addr;

assign ifu_ib_wreq      = ib_wreq     ;
assign ifu_ib_pc_data   = ib_pc_data  ;
assign ifu_ib_exp_vld   = ib_exp_vld  ;
assign ifu_ib_exp_code  = ib_exp_code ;
assign ifu_ib_bp_taddr  = ib_bp_taddr ;
assign ifu_ib_bp_taken  = ib_bp_taken ;
assign ifu_ib_bp_bhtv   = ib_bp_bhtv  ;
assign ifu_ib_bp_phtv   = ib_bp_phtv  ;


wire iaddr_chg_en = ifetch_en || bru_flush;

//DFFs
dffr #(32,32'h8010_0000) pc_data_ff  (clk,rst_n,iaddr_chg_en,pc_data_nxt,pc_data);
dffr #(32,32'h8010_0000) inst_addr_ff(clk,rst_n,iaddr_chg_en,inst_addr_nxt,inst_addr);
dffr #(1)                 iaddr_vld_ff(clk,rst_n,iaddr_chg_en,iaddr_vld_nxt,iaddr_vld);

// Request
// dffr #(1)   ib_wreq_buf_ff      (clk,rst_n,1'b1,     ib_wreq_nxt     ,ib_wreq_buf    );
// dffr #(32)  ib_pc_data_buf_ff   (clk,rst_n,ifetch_en,ib_pc_data_nxt  ,ib_pc_data_buf );
// dffr #(1)   ib_exp_vld_buf_ff   (clk,rst_n,ifetch_en,ib_exp_vld_nxt  ,ib_exp_vld_buf );
// dffr #(5)   ib_exp_code_buf_ff  (clk,rst_n,ifetch_en,ib_exp_code_nxt ,ib_exp_code_buf);
// dffr #(32)  ib_bp_taddr_buf_ff  (clk,rst_n,ifetch_en,ib_bp_taddr_nxt ,ib_bp_taddr_buf);
// dffr #(1)   ib_bp_taken_buf_ff  (clk,rst_n,ifetch_en,ib_bp_taken_nxt ,ib_bp_taken_buf);
// dffr #(4)   ib_bp_bhtv_buf_ff   (clk,rst_n,ifetch_en,ib_bp_bhtv_nxt  ,ib_bp_bhtv_buf ); 
// dffr #(32)  ib_bp_phtv_buf_ff   (clk,rst_n,ifetch_en,ib_bp_phtv_nxt  ,ib_bp_phtv_buf ); 

// dffr #(1)   ib_wreq_ff          (clk,rst_n,1'b1,     ib_wreq_nxt    ,ib_wreq     );
// dffr #(32)  ib_pc_data_ff       (clk,rst_n,ifetch_en,ib_pc_data_buf ,ib_pc_data  );
// dffr #(1)   ib_exp_vld_ff       (clk,rst_n,ifetch_en,ib_exp_vld_buf ,ib_exp_vld  );
// dffr #(5)   ib_exp_code_ff      (clk,rst_n,ifetch_en,ib_exp_code_buf,ib_exp_code );
// dffr #(32)  ib_bp_taddr_ff      (clk,rst_n,ifetch_en,ib_bp_taddr_buf,ib_bp_taddr );
// dffr #(1)   ib_bp_taken_ff      (clk,rst_n,ifetch_en,ib_bp_taken_buf,ib_bp_taken );
// dffr #(4)   ib_bp_bhtv_ff       (clk,rst_n,ifetch_en,ib_bp_bhtv_buf ,ib_bp_bhtv  ); 
// dffr #(32)  ib_bp_phtv_ff       (clk,rst_n,ifetch_en,ib_bp_phtv_buf ,ib_bp_phtv  ); 

dffr #(1)   ib_wreq_ff          (clk,rst_n,1'b1,     ib_wreq_nxt    ,ib_wreq     );
dffr #(32)  ib_pc_data_ff       (clk,rst_n,ifetch_en,ib_pc_data_nxt ,ib_pc_data  );
dffr #(1)   ib_exp_vld_ff       (clk,rst_n,ifetch_en,ib_exp_vld_nxt ,ib_exp_vld  );
dffr #(5)   ib_exp_code_ff      (clk,rst_n,ifetch_en,ib_exp_code_nxt,ib_exp_code );
dffr #(32)  ib_bp_taddr_ff      (clk,rst_n,ifetch_en,ib_bp_taddr_nxt,ib_bp_taddr );
dffr #(1)   ib_bp_taken_ff      (clk,rst_n,ifetch_en,ib_bp_taken_nxt,ib_bp_taken );
dffr #(4)   ib_bp_bhtv_ff       (clk,rst_n,ifetch_en,ib_bp_bhtv_nxt ,ib_bp_bhtv  ); 
dffr #(32)  ib_bp_phtv_ff       (clk,rst_n,ifetch_en,ib_bp_phtv_nxt ,ib_bp_phtv  ); 


endmodule


